package club.janna.hello.factory;

import club.janna.hello.frame.Aux;
import club.janna.hello.frame.DataUnit;
import club.janna.hello.frame.GDW376D1;
import club.janna.hello.parser.constants.GDW376D1Statute;

import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.List;

/**
 * Created by guopanbo on 18/4/13.
 */
public class GDW376D1FrameFactory {

    /**
     * 响应帧序号
     */
    private static short rseq = 0;

    /**
     * 生成确认
     * @param f
     * @return
     */
    private static GDW376D1 confirm(GDW376D1 f) {
        if(f == null)
            return null;
        GDW376D1 nf = new GDW376D1();
        nf.setStart(GDW376D1Statute.START);
        nf.setStart1(GDW376D1Statute.START);

        nf.setLength(0);
        nf.setLength1(0);

        //控制域
        nf.setCtl((short)((0x0 << 4) | GDW376D1Statute.FUNCTION_RES_CODE_LINK_TEST));

        //地址域
        nf.setAreaCode(f.getAreaCode());
        nf.setAddress(f.getAddress());
        nf.setAddressMark((short)0);

        //用户数据区
        nf.setAfn(GDW376D1Statute.AFN_CONFIRM);
        nf.setSeq((short)(0x60 | rseq));

        //增加计数
        increaseRseq();

        List<DataUnit> dataUnitList = new ArrayList<>();
        DataUnit du = new DataUnit();
        du.setDa1("0");
        du.setDa2((short)0);
        du.setDt1("1");
        du.setDt2((short)0);
        dataUnitList.add(du);
        nf.setDataUnits(dataUnitList);

        //nf.setcAfc(f.getAfn());
        //nf.setcDataUnits(f.getDataUnits());
        //nf.setcErr((short)0);

        nf.setCrc((short)0);

        nf.setEnd(GDW376D1Statute.END);

        return nf;
    }

    /**
     * 生成查询数据
     * @param address
     * @param areaCode
     * @return
     */
    private static GDW376D1 queryData(int areaCode, int address, int pn, int fn) {
        GDW376D1 nf = new GDW376D1();
        nf.setStart(GDW376D1Statute.START);
        nf.setStart1(GDW376D1Statute.START);

        nf.setLength(0);
        nf.setLength1(0);

        //控制域
        nf.setCtl((short)((0b0100 << 4) | GDW376D1Statute.FUNCTION_REQ_QUERY1));

        //地址域
        nf.setAreaCode(areaCode);
        nf.setAddress(address);
        nf.setAddressMark((short)0);

        //用户数据区
        nf.setAfn(GDW376D1Statute.AFN_QUERY1);
        nf.setSeq((short)(0xe0 | (rseq & 0x0f)));

        //增加计数
        increaseRseq();

        List<DataUnit> dataUnitList = new ArrayList<>();
        DataUnit du = new DataUnit();
        //pn
        du.setDa1(GDW376D1Statute.getDa1ByP(pn));
        du.setDa2(GDW376D1Statute.getDa2ByP(pn));
        //fn
        du.setDt1(GDW376D1Statute.getDt1ByF(fn));
        du.setDt2(GDW376D1Statute.getDt2ByF(fn));
        dataUnitList.add(du);
        nf.setDataUnits(dataUnitList);

        //附加信息域
        Aux aux = new Aux();
        aux.setTp(GDW376D1Statute.generateTp16(rseq));
        nf.setAux(aux);

        nf.setCrc((short)0);

        nf.setEnd(GDW376D1Statute.END);

        return nf;
    }

    /**
     * 计数器增加
     */
    private static void increaseRseq() {
        rseq ++;
        if(rseq == 0xff) {
            rseq = 0;
        }
    }

    /**
     * 报文转10进制数组
     * @param nf
     * @return
     */
    private static int[] frame2DecArray(GDW376D1 nf) {
        if(nf == null)
            return null;
        int[] dest = new int[64];
        int offset = 0;
        Field[] fields = GDW376D1.class.getDeclaredFields();
        if(fields != null)
            for(Field field : fields)
                if(field != null) {
                    try {
                        field.setAccessible(true);
                        if(field.get(nf) != null) {
                            if (field.getType() == Byte.class) {
                                dest[offset++] = (byte)field.get(nf);
                            } else if(field.getType() == Short.class) {
                                dest[offset++] = (short)field.get(nf);
                            } else if(field.getType() == Integer.class) {
                                int t = (int)field.get(nf);
                                dest[offset ++] = t & 0xff;
                                dest[offset ++] = t >> 8 & 0xff;
                            } else if(field.getType() == List.class) {
                                List<DataUnit> dataUnits = (List<DataUnit>) field.get(nf);
                                for (DataUnit du : dataUnits)
                                    if (du != null) {
                                        dest[offset ++] = Integer.parseInt(du.getDa1(), 2);
                                        dest[offset ++] = Integer.valueOf(du.getDa2());
                                        dest[offset ++] = Integer.parseInt(du.getDt1(), 2);
                                        dest[offset ++] = Integer.valueOf(du.getDt2());
                                    }
                            } else if(field.getType() == Aux.class) {
                                Aux aux = (Aux)field.get(nf);
                                if(aux != null) {
                                    if(aux.getTp() != null) {
                                        System.arraycopy(aux.getTp(), 0, dest, offset, aux.getTp().length);
                                        offset += aux.getTp().length;
                                    }
                                }
                            }
                        }
                        //System.out.println(field.get(nf));
                    } catch (IllegalAccessException e) {
                        e.printStackTrace();
                    }
                }
        if(offset == 0)
            return null;
        int[] a = new int[offset];
        System.arraycopy(dest, 0, a, 0, offset);

        //计算长度
        a[1] = (a.length - 8) << 2 | 0b10 & 0xff;
        a[2] = ((a.length - 8) << 2 | 0b10) >> 8 & 0xff;
        a[3] = a[1];
        a[4] = a[2];

        //计算校验位
        a[a.length - 2] = GDW376D1Statute.CRC(a) & 0xff;

        return a;
    }

    /**
     * 生成确认的报文
     * @param f
     * @return
     */
    public static int[] generateConfirm(GDW376D1 f) {
        return frame2DecArray(confirm(f));
    }

    /**
     * 生成查询数据的报文
     * @param areaCode
     * @param address
     * @return
     */
    public static int[] generateQueryData(int areaCode, int address, int pn, int fn) {
        return frame2DecArray(queryData(areaCode, address, pn, fn));
    }
}
